#ifndef CODEGENERATOR_H_
#define CODEGENERATOR_H_

#include "Defines.h"
#include "ErrorPrinter.h"
#include "ExpressionTree.h"
#include <cctype>
#include <stack>

#define LOAD_VAR    0
#define STORE_VAR   1
#define PARAM_VAR   2

namespace LALG
{

enum CmdType { CONST, VAR, PROC, READLN, WRITELN, CALL, ATTR, IF, ELSE, ENDIF, WHILE, ENDWHILE, REPEAT, UNTIL };

/*-----------------------------------------------------------------------------
 *  Argumentos de comandos
 *-----------------------------------------------------------------------------*/
typedef list<string> CmdArg;

/*-----------------------------------------------------------------------------
 *  Linha de comando
 *-----------------------------------------------------------------------------*/
struct CmdLine
{
    CmdType type;
    CmdArg arg;
};
typedef shared_ptr<CmdLine> CmdLinePtr;

/*-----------------------------------------------------------------------------
 *  Linhas a serem colocadas o endereço do procedimento
 *-----------------------------------------------------------------------------*/
struct ProcPtr
{
	uint addr;
	string name;
};

/*-----------------------------------------------------------------------------
 *  Código
 *-----------------------------------------------------------------------------*/
typedef vector<CmdLinePtr> Code;
typedef shared_ptr<Code> CodePtr;

/*
 * =====================================================================================
 *        Class:  CodeGenerator
 *  Description:  Classe que representa um gerador de código
 * =====================================================================================
 */
class CodeGenerator
{

public:

     /*-----------------------------------------------------------------------------
     *  Constrói um gerador de código
     *-----------------------------------------------------------------------------*/
    CodeGenerator(ErrorPrinter &_errors) : errors(_errors), mainCode(new Code)
    {
        code = mainCode.get(); currentLine = 1;
    }

    /*-----------------------------------------------------------------------------
     *  Gera o código de saída
     *-----------------------------------------------------------------------------*/
    void generate(ofstream &of);

    /*-----------------------------------------------------------------------------
     *  Adiciona uma constante
     *-----------------------------------------------------------------------------*/
    void addConstant(const string &name, const VarValue &value);

    /*-----------------------------------------------------------------------------
     *  Adiciona uma variável
     *-----------------------------------------------------------------------------*/
    void addVariable(const string &name);

    /*-----------------------------------------------------------------------------
     *  Adiciona um readln
     *-----------------------------------------------------------------------------*/
    void addReadln(const string &var);

    /*-----------------------------------------------------------------------------
     *  Adiciona um writeln
     *-----------------------------------------------------------------------------*/
    void addWriteln(const string &var);

    /*-----------------------------------------------------------------------------
     *  Adiciona um procedimento
     *-----------------------------------------------------------------------------*/
    void addProcedure(const string &name, const VarList &vlist);

    /*-----------------------------------------------------------------------------
     *  Adiciona uma atribuição
     *-----------------------------------------------------------------------------*/
    void addAttribution(const string &name, const ExpressionTree &exp);

    /*-----------------------------------------------------------------------------
     *  Adiciona um procedimento
     *-----------------------------------------------------------------------------*/
    void startProcedure(const string &name, const VarList &vlist);

    /*-----------------------------------------------------------------------------
     *  Encerra um procedimento
     *-----------------------------------------------------------------------------*/
    void endProcedure();

    /*-----------------------------------------------------------------------------
     *  Começa um if
     *-----------------------------------------------------------------------------*/
    void startIf(const ExpressionTree &exp);

    /*-----------------------------------------------------------------------------
     *  Começa um else
     *-----------------------------------------------------------------------------*/
    void startElse();

    /*-----------------------------------------------------------------------------
     *  Começa um while
     *-----------------------------------------------------------------------------*/
    void startWhile(const ExpressionTree &exp);

    /*-----------------------------------------------------------------------------
     *  Encerra um if
     *-----------------------------------------------------------------------------*/
    void endIf();

    /*-----------------------------------------------------------------------------
     *  Encerra um while
     *-----------------------------------------------------------------------------*/
    void endWhile();

    /*-----------------------------------------------------------------------------
     *  Começa um repeat
     *-----------------------------------------------------------------------------*/
    void startRepeat();

    /*-----------------------------------------------------------------------------
     *  Encerra um repeat
     *-----------------------------------------------------------------------------*/
    void endRepeat(const ExpressionTree &exp);

    /*-----------------------------------------------------------------------------
	 *  Obtem o codigo corrente
	 *-----------------------------------------------------------------------------*/
    Code * getCode() { return code; }

private:

    /*-----------------------------------------------------------------------------
     *  Gera o código de um procedimento ou função principal
     *-----------------------------------------------------------------------------*/
    void generateCode(Code *currentCode, bool procedure = false, uint nAllocs = 0);

    /*-----------------------------------------------------------------------------
     *  Gera o código de uma expressão
     *-----------------------------------------------------------------------------*/
    void generateExpression(list<string>::iterator &it);

    /*-----------------------------------------------------------------------------
     *  Gera o valor de uma variável
     *-----------------------------------------------------------------------------*/
    void generateVariable(const string &name, int type = LOAD_VAR);

    /*-----------------------------------------------------------------------------
     *  Adiciona um comando
     *-----------------------------------------------------------------------------*/
    void addCommand(CmdLinePtr cmd);

    /*-----------------------------------------------------------------------------
     *  Retorna a linha corrente do código objeto
     *-----------------------------------------------------------------------------*/
    uint curLine() { return objCode.size() - 1; }

    /*-----------------------------------------------------------------------------
     *  Conteiner de erros
     *-----------------------------------------------------------------------------*/
    ErrorPrinter &errors;

    /*-----------------------------------------------------------------------------
     *  Código corrente
     *-----------------------------------------------------------------------------*/
    Code* code;

    /*-----------------------------------------------------------------------------
     *  Código principal
     *-----------------------------------------------------------------------------*/
    CodePtr mainCode;

    /*-----------------------------------------------------------------------------
     *  Código dos procedimentos
     *-----------------------------------------------------------------------------*/
    unordered_map<string, CodePtr> procedures;

    /*-----------------------------------------------------------------------------
     *  Valores de constantes
     *-----------------------------------------------------------------------------*/
    unordered_map<string, string> constValues;

    /*-----------------------------------------------------------------------------
     *  Posição de memória das variáveis
     *-----------------------------------------------------------------------------*/
    unordered_map<string, int> varAddrs;

    /*-----------------------------------------------------------------------------
     *  Pilha de endereços vazios que deverão ser preenchidos
     *-----------------------------------------------------------------------------*/
    stack<int> emptyAddrs;

    /*-----------------------------------------------------------------------------
	 *  Posição de memória das procedures
	 *-----------------------------------------------------------------------------*/
	unordered_map<string, int> procAddrs;

	/*-----------------------------------------------------------------------------
     *  Endereço da main
     *-----------------------------------------------------------------------------*/
	int mainAddr;

	/*-----------------------------------------------------------------------------
	 *  Linha atual
	 *-----------------------------------------------------------------------------*/
    int currentLine;

    /*-----------------------------------------------------------------------------
	 *  Linhas a serem realizadas o post-processing para colocaçao de endereços
	 *-----------------------------------------------------------------------------*/
    list<ProcPtr> update;

    /*-----------------------------------------------------------------------------
     *  Código objetivo
     *-----------------------------------------------------------------------------*/
    vector<string> objCode;

};

}

#endif /* CODEGENERATOR_H_ */
